package com.changgou.search.service.impl;


import com.alibaba.fastjson.JSON;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SearchService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService{

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    //按照查询条件进行数据查询
    public Map search(Map<String, String> searchMap) {

        Map<String,Object> resultMap =new HashMap<>();

       //构建查询条件封装对象  NativeSearchQuery ：是springdata中的查询条件
        NativeSearchQueryBuilder nativeSearchQueryBuilder=new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        //按照关键字查询  matchQuery 是模糊查询
        if (StringUtils.isNotEmpty(searchMap.get("keywords"))){
            boolQuery.must(QueryBuilders.matchQuery("name",searchMap.get("keywords")).operator(Operator.AND));
        }

        //按照品牌进行过滤查询  判断有没有brand termQuery 是完全匹配
        if(StringUtils.isNotEmpty(searchMap.get("brand"))){
            boolQuery.filter(QueryBuilders.termQuery("brandName",searchMap.get("brand")));
        }

        //按照规格进行过滤查询 与前端约定好的再规格前面加上spec_ 作为规格
        for (String key : searchMap.keySet()) {
            if(key.startsWith("spec_")){
                //使用get请求时在网络传输中
                String value =searchMap.get(key).replace("%2B","+");
                boolQuery.filter(QueryBuilders.termQuery(("specMap."+key.substring(5)+".keyword"),value));
            }
        }

        //按照价格进行区间的过滤查询
        if(StringUtils.isNotEmpty(searchMap.get("price"))){
            String[] prices = searchMap.get("price").split("-");
            if(prices.length==2){
                boolQuery.filter(QueryBuilders.rangeQuery("price").lte(prices[1]));
            }
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(prices[0]));
        }

        //用于建造一个NativeSearchQuery查询对象
        nativeSearchQueryBuilder.withQuery(boolQuery);

        //按照品牌进行分组, 聚合查询
        String skuBrand ="skuBrand";

        //addAggregation 添加聚合  field:来操作当前要分组的域 brandName
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuBrand).field("brandName"));

        //按照规格进行聚合查询
        String skuSpec ="skuSpec";
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuSpec).field("spec.keyword"));

        //开启分页查询 当前页 pageNum 和 每页显示的条数 pageSize
        String pageNum =searchMap.get("pageNum");
        String pageSize=searchMap.get("pageSize");

        //如果前端没有传入这两个参数,就设置默认值
        if (StringUtils.isEmpty(searchMap.get("pageNum"))){
            pageNum="1";
        }
        if(StringUtils.isEmpty(searchMap.get("pageSize"))){
            pageSize="30";
        }

        //设置分页 第一参数是当前页 从0开始 第二个参数是每页显示多少条
        nativeSearchQueryBuilder.withPageable(PageRequest.of(Integer.parseInt(pageNum)-1,Integer.parseInt(pageSize)));

        //按照相关字段进行排序查询
        //1.当前的域:sortField  是升序还是降序:sortRule
        if(StringUtils.isNotEmpty(searchMap.get("sortField"))&&StringUtils.isNotEmpty(searchMap.get("sortRule"))){
            if("ASC".equals(searchMap.get("sortRule"))){
                //升序
                nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort((searchMap.get("sortField"))).order(SortOrder.ASC));
            }else {
                //降序
                nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort((searchMap.get("sortField"))).order(SortOrder.DESC));
            }
        }

        //设置高亮域以及高亮的样式
        HighlightBuilder.Field field =new HighlightBuilder.Field("name") //高亮域
                .preTags("<span style='color:red'>")//高亮样式的前缀
                .postTags("</span>");//高亮样式的后缀
        nativeSearchQueryBuilder.withHighlightFields(field);

   //构建查询
        if(searchMap!=null){
            //<T> Page<T> queryForPage(SearchQuery var1, Class<T> var2, SearchResultMapper var3);
            /*
            * 三个参数:第一个是SearchQuery 查询条件  第二个是 Class<T> 要封装成的类
            *  第三个是SearchResultMapper 查询的结果对象
            * */
            //开启查询
            AggregatedPage<SkuInfo> resultInfo = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
                @Override
                public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                    //查询结果的相关操作
                    List<T> list =new ArrayList<>();

                    //获取查询命中结果数
                    SearchHits hits = searchResponse.getHits();
                    if(hits!=null){
                        //查询到了数据
                        for (SearchHit searchHit : hits) {
                            //searchHit 是索引库中的每个商品 searchHit转换成skuInfo
                            SkuInfo skuInfo = JSON.parseObject(searchHit.getSourceAsString(), SkuInfo.class);

                            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();

                            if(highlightFields!=null&&highlightFields.size()>0){
                                //有高亮域
                                skuInfo.setName(highlightFields.get("name").getFragments()[0].toString());
                            }

                            list.add((T)skuInfo);
                        }
                    }
                    return new AggregatedPageImpl<T>(list,pageable,hits.getTotalHits(),searchResponse.getAggregations());
                }
            });
            //封装最终的查询结果
            //总记录数
            resultMap.put("total",resultInfo.getTotalElements());

            //总页数
            resultMap.put("totalPages",resultInfo.getTotalPages());

            //数据的集合
            resultMap.put("rows",resultInfo.getContent());

            //封装品牌的分组查询结果
            StringTerms brandTerms =(StringTerms)resultInfo.getAggregation(skuBrand);
            //因为结果要返回到前端,不能直接返回,需要继续转换
            List<String> brandList = brandTerms.getBuckets().stream().map(bucket ->
                    bucket.getKeyAsString()).collect(Collectors.toList());

            resultMap.put("brandList",brandList);

            //封装规格的分组查询结果
            StringTerms specTerms = (StringTerms) resultInfo.getAggregation(skuSpec);
            List<String> specList = specTerms.getBuckets().stream().map(bucket ->
                    bucket.getKeyAsString()).collect(Collectors.toList());
            resultMap.put("specList",this.formartSpec(specList));

            //封装当前页
            resultMap.put("pageNum",pageNum);

            return resultMap;
        }
        //封装查询的结果
        return null;
    }

    /**
     * 原有数据
     *  [
     *         "{'颜色': '黑色', '尺码': '平光防蓝光-无度数电脑手机护目镜'}",
     *         "{'颜色': '红色', '尺码': '150度'}",
     *         "{'颜色': '黑色', '尺码': '150度'}",
     *         "{'颜色': '黑色'}",
     *         "{'颜色': '红色', '尺码': '100度'}",
     *         "{'颜色': '红色', '尺码': '250度'}",
     *         "{'颜色': '红色', '尺码': '350度'}",
     *         "{'颜色': '黑色', '尺码': '200度'}",
     *         "{'颜色': '黑色', '尺码': '250度'}"
     *     ]
     *
     *    需要的数据格式
     *    {
     *        颜色:[黑色,红色],
     *        尺码:[100度,150度]
     *    }
     */

    public Map<String,Set<String>> formartSpec(List<String> specList){
        Map<String,Set<String>> resultMap =new HashMap<>();

        if(specList!=null&&specList.size()>0){
            //集合中存在数据
            for (String specJSON : specList) {
                Map<String,String> specMap = JSON.parseObject(specJSON, Map.class);
                for (String key : specMap.keySet()) {
                    Set<String> specSet = resultMap.get(key);
                    if(specSet==null){
                        specSet=new HashSet<String>();
                    }
                    //将规格数据放入set中
                    specSet.add(specMap.get(key));

                    //将specSet放入map中
                    resultMap.put(key,specSet);
                }
            }
        }
        return resultMap;
    }
}
